import { Command } from 'commander';
import inquirer from 'inquirer';
import semver from 'semver';
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { api } from '../services/api';
import { logger } from '../utils/logger';
import { createTable, formatDate } from '../utils/table';
import { requireAuth, validateVersion } from '../utils/validation';
import { CLIError } from '../types';

export function publishCommands(program: Command): void {
  program
    .command('version [type]')
    .description('Bump version (patch|minor|major)')
    .action(async (type?: string) => {
      try {
        const packagePath = join(process.cwd(), 'package.json');
        const cmodelPath = join(process.cwd(), 'cmodel.json');
        
        let packageJson: any;
        let cmodelJson: any;
        
        try {
          packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
          cmodelJson = JSON.parse(readFileSync(cmodelPath, 'utf8'));
        } catch (error) {
          throw new CLIError('Not in a module directory. Make sure package.json and cmodel.json exist.');
        }

        const currentVersion = packageJson.version || '1.0.0';
        
        let newVersion: string;
        if (type && ['patch', 'minor', 'major'].includes(type)) {
          newVersion = semver.inc(currentVersion, type as semver.ReleaseType) || '';
        } else {
          const choices = [
            { name: `Patch (${semver.inc(currentVersion, 'patch')})`, value: 'patch' },
            { name: `Minor (${semver.inc(currentVersion, 'minor')})`, value: 'minor' },
            { name: `Major (${semver.inc(currentVersion, 'major')})`, value: 'major' },
            { name: 'Custom', value: 'custom' }
          ];

          const answer = await inquirer.prompt([
            {
              type: 'list',
              name: 'versionType',
              message: `Current version is ${currentVersion}. Select version bump:`,
              choices
            }
          ]);

          if (answer.versionType === 'custom') {
            const customAnswer = await inquirer.prompt([
              {
                type: 'input',
                name: 'version',
                message: 'Enter custom version:',
                validate: (input: string) => {
                  if (!validateVersion(input)) {
                    return 'Please enter a valid semantic version (e.g., 1.0.0)';
                  }
                  if (semver.lte(input, currentVersion)) {
                    return 'New version must be higher than current version';
                  }
                  return true;
                }
              }
            ]);
            newVersion = customAnswer.version;
          } else {
            newVersion = semver.inc(currentVersion, answer.versionType) || '';
          }
        }

        if (!newVersion) {
          throw new CLIError('Failed to generate new version');
        }

        packageJson.version = newVersion;
        cmodelJson.version = newVersion;

        writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
        writeFileSync(cmodelPath, JSON.stringify(cmodelJson, null, 2));

        logger.success(`Version bumped to ${newVersion}`);
        logger.info('Updated files: package.json, cmodel.json');
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to update version');
      }
    });

  program
    .command('publish')
    .description('Publish module version')
    .option('--tag <tag>', 'Version tag (dev, staging, prod)', 'dev')
    .action(async (options) => {
      try {
        requireAuth();
        
        const packagePath = join(process.cwd(), 'package.json');
        let packageJson: any;
        
        try {
          packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
        } catch (error) {
          throw new CLIError('Not in a module directory. Make sure package.json exists.');
        }

        const moduleName = packageJson.name;
        const version = packageJson.version;
        const tag = options.tag;

        if (!moduleName || !version) {
          throw new CLIError('Module name and version are required in package.json');
        }

        const confirm = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'publish',
            message: `Publish ${moduleName}@${version} with tag "${tag}"?`,
            default: false
          }
        ]);

        if (!confirm.publish) {
          logger.info('Publish cancelled');
          return;
        }

        const spinner = logger.spinner(`Publishing ${moduleName}@${version}...`);
        try {
          await api.publishModule(moduleName, version, tag);
          spinner.succeed();
          
          logger.success(`Module ${moduleName}@${version} published successfully!`);
          logger.info(`Tag: ${tag}`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to publish module');
      }
    });

  program
    .command('promote')
    .description('Promote version between tags')
    .option('--from <tag>', 'Source tag')
    .option('--to <tag>', 'Target tag')
    .requiredOption('-m, --module <module>', 'Module name')
    .action(async (options) => {
      try {
        requireAuth();

        const { module: moduleName, from: fromTag, to: toTag } = options;
        
        if (!fromTag || !toTag) {
          throw new CLIError('Both --from and --to tags are required');
        }

        const confirm = await inquirer.prompt([
          {
            type: 'confirm',
            name: 'promote',
            message: `Promote ${moduleName} from "${fromTag}" to "${toTag}"?`,
            default: false
          }
        ]);

        if (!confirm.promote) {
          logger.info('Promotion cancelled');
          return;
        }

        const spinner = logger.spinner(`Promoting ${moduleName} from ${fromTag} to ${toTag}...`);
        try {
          await api.promoteVersion(moduleName, fromTag, toTag);
          spinner.succeed();
          
          logger.success(`Module ${moduleName} promoted from ${fromTag} to ${toTag}`);
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to promote module');
      }
    });

  program
    .command('history <module>')
    .description('View module version history')
    .action(async (moduleName: string) => {
      try {
        requireAuth();

        const spinner = logger.spinner(`Fetching version history for ${moduleName}...`);
        try {
          const versions = await api.getModuleVersions(moduleName);
          spinner.succeed();

          if (versions.length === 0) {
            logger.info(`No versions found for module "${moduleName}"`);
            return;
          }

          const headers = ['Version', 'Tag', 'Author', 'Created', 'Changelog'];
          const rows = versions.map(version => [
            version.version,
            version.tag,
            version.author,
            formatDate(version.createdAt),
            (version.changelog || '').substring(0, 50) + (version.changelog && version.changelog.length > 50 ? '...' : '')
          ]);

          logger.log(`\nVersion history for ${moduleName}:`);
          logger.log(createTable(headers, rows));
        } catch (error) {
          spinner.fail();
          throw error;
        }
      } catch (error) {
        if (error instanceof CLIError) {
          throw error;
        }
        throw new CLIError('Failed to fetch version history');
      }
    });
}